package PokerComponents;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.Timer;

import BlindCounters.DoubleBlindCounter;
import DataStructures.Card;
import DataStructures.Deck;
import DataStructures.PokerTable;
import Network.Announcement;
import Network.AnnouncementInterface;
import Network.DealerMonitor;
import Network.RemotePokerPlayer;

/**
 * This class contains most of the logic for a game of poker. It interacts with the deck and table and controls 
 * the flow of the game. That is, whose turn it is, checking if their action is valid, originating control messsages, etc
 * @author Phil
 *
 */
public class HoldEmDealer implements ActionListener{

	 protected PokerTable table;
	 protected Deck deck;
	 protected int handNumber;
	 protected int startTime;
	 protected DoubleBlindCounter timer;
	 protected boolean isGameRunning;
	 
	 protected Timer turnTimer;
	 protected PlayerIterator pIter;
	 protected AnnouncementInterface announce;
	 protected int stackSize;
	 
	public HoldEmDealer(PokerTable table, DoubleBlindCounter timer, int turnTime, int stackSize){
		this.table = table;
		this.deck = new Deck();
		this.handNumber = 0;
		this.startTime = 0;
		this.timer = timer;
		this.isGameRunning = false;
		this.turnTimer = new Timer(turnTime*1000, this);
		this.stackSize = stackSize;
	}
	
	/**
	 * This method will return true if the game is over, which is when only one person is left
	 * @return
	 */
	public boolean isGameOver(){
		if(table.getEmptySeats().length <= 1){
			return true;
		}
		return false;
	}
	
	
	/**
	 * This method will begin the poker game. 
	 */
	public void playGame(){		
		if(isGameOver()){
			return;
		}
		
		initializeGame();
		
		while(!isGameOver()){
			handNumber++;
			announce.announceNewHand(table.getDealerButtonLocation(), handNumber, table.getTableId());
			preflop();
			turn();
			river();
			handResolution();
		}
		
		// TODO: get the game end time to see how long the game took.
		
	}
	
	/**
	 * A method to prepare the game for the first hand. This method will place the 
	 * dealer button and start the blind timer.
	 */
	private void initializeGame(){
		// place dealer button to a random player
		int[] takenSeats = table.getTakenSeats();
		int randomIndex = ( int ) ( Math.random() * takenSeats.length ); // Pick a random taken seat to place the button
		table.placeDealerButton(takenSeats[randomIndex]);
		
		// initialize the player iterator 
		pIter = new PlayerIterator(table.getPlayers(), table.getPlayerUnderTheGun().getSeatNumber());
		
		// announce the game has started
		announce.announceNewGame(table.getTableSize(), pIter.size(), stackSize);
		
		// announce the players to everyone
		// TODO: check this is the correct place for this announcement
		announce = new Announcement(table.getPlayers());
		
		// deal hole cards
		dealHoleCards();
		
		// announce blinds
		announce.announceSetBlind(timer.getSmallBlindSize(), timer.getBigBlindSize(), 0);
		
		// Start the blind timer
		timer.startBlindTimer();
	}
	
	/**
	 * This method hands out 2 cards to each player at the table.  
	 */
	private void dealHoleCards(){
		for(int i=0; i<table.getNumberOfPlayers()*2; i++){
			pIter.gotoNextPlayer();
			pIter.getActivePlayer().addCard(deck.drawCard());			
		}
	}	
	
	/**
	 * A method to perform the preflop actions. That is, place the players blinds and hand out their pocket cards. Then,
	 * ask each player which action they wish to perform until the round is concluded.
	 * @return 
	 */
	private void preflop(){
		// Get big/small blinds sizes
		int bigBlind = timer.getBigBlindSize();
		int smallBlind = timer.getSmallBlindSize();
		
		// Place Big Blind
		int amount = 0;
		amount = table.getPlayerInBigBlind().removeChips(bigBlind);
		table.placeChipsInFrontOfPlayer(table.getPlayerInBigBlind().getSeatNumber(), amount);
		
		// Place Small Blind
		amount = table.getPlayerInSmallBlind().removeChips(smallBlind);
		table.placeChipsInFrontOfPlayer(table.getPlayerInSmallBlind().getSeatNumber(), amount);
		
		// hand out cards
		dealHoleCards();
		
		// Play a round
		playRound();
	}
	
	private void flop(){
		Card[] cards = new Card[3];
		cards[0] = deck.drawCard();
		cards[1] = deck.drawCard();
		cards[2] = deck.drawCard();
		putCardsOnTable(cards);
		announce.announceFlop(cards);
		playRound();
	}
	
	private void turn(){
		Card[] c = new Card[1]; 
		c[0] = deck.drawCard();
		putCardsOnTable(c);
		announce.announceTurn(c[0]);
		playRound();
	}
	
	private void river(){
		Card[] c = new Card[1]; 
		c[0] = deck.drawCard();
		putCardsOnTable(c);
		announce.announceTurn(c[0]);
		playRound();
	}

	private void handResolution(){
		// shows hands as necessary
		
		// determine hand winner
		
		// award chips
		
		// clear table
		table.clearTable();
		
		// reset the deck
		deck.resetDeck();
		
		// see if any players need to be removed
		RemotePokerPlayer[] players = table.getPlayers();
		for(RemotePokerPlayer p : players){
			if(p.getChipCount() <= 0){
				table.removePlayer(p.getSeatNumber());
			}
		}
		
		// reset active player for next hand
		int tmpSeat = table.getPlayerInSmallBlind().getSeatNumber();
		while(!pIter.isPlayerInHand(tmpSeat)){
			tmpSeat++;
			tmpSeat =+ pIter.size();
		}		
		pIter.setActivePlayer(tmpSeat);
		
		// move dealer button
		table.passDealerButtonClockwise();
		
		// hand is over, announce this
		//announce.announceHandEnd(numberOfWinners, playerIds, rewardedChips);
	}
	
	/**
	 * This method will place the specified number of cards on the table by drawing from the deck
	 * @param numberOfCards
	 */
	private void putCardsOnTable(Card[] cards){
		for(Card c : cards){
			table.placeCardOnTable(c);
		}		
	}
	
	/**
	 * This method will iterate over each player in the hand allowing them to take turns until the hand is over.
	 * @param pIter
	 * @return
	 */
	private void playRound(){		
		// The hand is over once the "endOfHandSeat" player is reached again. Note: this seat number
		// can change throughout the hand. 
		int endOfHandSeat = pIter.getActivePlayer().getSeatNumber();
			
		
		int waitingOnPlayer = pIter.getActivePlayer().getSeatNumber();
		do{
			// prompt the active player
			//pIter.getActivePlayer().prompt();
			turnTimer.start();
			
			// repeatedly sleep and check if the active player has changed
			while(waitingOnPlayer == pIter.getActivePlayer().getSeatNumber()){
				try {
					this.wait(50);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			turnTimer.stop();
						
		}while(pIter.getActivePlayer().getSeatNumber() != endOfHandSeat);
		
	}

	
		
	
	@Override
	public void actionPerformed(ActionEvent arg0) {
		// Received an event, this indicates currnet players turn is over
		turnTimer.stop();
		announce.announceFold(pIter.getActivePlayer().getPlayerId());
		pIter.gotoNextPlayer();
	}

	
}
